﻿/*
 * QtPet Online File Manager v1.0
 * Copyright (c) 2009, Zhifeng Lin (fszlin[at]gmail.com)
 * 
 * Licensed under the MS-PL license.
 * http://qtfile.codeplex.com/license
 */

using System;
using System.IO;
using System.Web;
using System.Linq;
using System.Web.Mvc;

using QtFile.Services;
using QtFile.Repositories;

namespace QtFile.Controllers
{
	public class UserFileController : Controller
	{
		/// <summary>
		/// Represents the foder path used to contruct file based services while they are not provided.
		/// </summary>
	    private const string DefaultFilePath = "~/UserFiles";

	    private IUserSettingRepository settingRepository;

		private IFileServiceProvider serviceProvider;

	    public virtual IUserSettingRepository SettingRepository
	    {
	        get
	        {
	            if (settingRepository == null)
	                settingRepository = new ConfigUserSettingRepository();
	            return settingRepository;
	        }
	    }

	    public virtual IFileServiceProvider ServiceProvider
	    {
	        get
	        {
	            if (serviceProvider == null)
	                serviceProvider = new FileBased.FileServiceProvider(MapPath(DefaultFilePath));
	            return serviceProvider;
	        }
	    }

		public virtual string MapPath(string path)
		{
			return Server.MapPath(path);
		}

		public virtual string Username
		{
			get
			{
				return User.Identity.Name;
			}
		}

		public UserFileController()
		{
		}

		public UserFileController(IUserSettingRepository settingRepository, IFileServiceProvider serviceProvider)
		{
			this.settingRepository = settingRepository;
			this.serviceProvider = serviceProvider;
		}

		[Authorize]
		public virtual ActionResult FileManager(string folder)
		{
			var fileService = ServiceProvider.GetFileService(Username);
			var folderService = ServiceProvider.GetFolderService(Username);

			var model = new FileMgrViewData()
			{
				Folders = folderService.GetAllFolders(),
				CurrentFolder = folder,
				Files = fileService.GetFileList(folder ?? String.Empty)
			};
			return View(model);
		}

		[Authorize]
		public ActionResult FolderCreate(string path)
		{
			var result = OpResult.Denied;

			try
			{
				if (OnFolderCreating(path, ref result))
				{
					var setting = SettingRepository.GetSetting(Username);
					var folderService = ServiceProvider.GetFolderService(Username);

					if (folderService.Count() >= setting.MaxFolders)
						result = OpResult.FolderLimitExceed;
					else
						result = folderService.Create(path);
				}
			}
			catch (Exception e)
			{
				result = OnError(e);
			}

			return OperationResult(result);
		}

		[Authorize]
		public ActionResult FolderMove(string srcPath, string destPath)
		{
			var result = OpResult.Denied;

			try
			{
				if (OnFolderMoving(srcPath, destPath, ref result))
				{
					var folderService = ServiceProvider.GetFolderService(Username);

					result = folderService.Move(srcPath, destPath);
				}
			}
			catch (Exception e)
			{
				result = OnError(e);
			}

			return OperationResult(result);
		}

		[Authorize]
		public ActionResult FolderDelete(string path)
		{
			var result = OpResult.Denied;

			try
			{
				if (OnFolderDeleting(path, ref result))
				{
					var folderService = ServiceProvider.GetFolderService(Username);

					result = folderService.Delete(path);
				}
			}
			catch (Exception e)
			{
				result = OnError(e);
			}

			return OperationResult(result);
		}

		[Authorize]
		public virtual ActionResult FolderList()
		{
			var result = OpResult.Denied;

			try
			{
				if (OnFolderListing(ref result))
				{
					var folderService = ServiceProvider.GetFolderService(Username);

					var folders = folderService.GetAllFolders();

					return Json(folders.Select(f => f.ToJson()));
				}

				return OperationResult(OpResult.Denied);
			}
			catch (Exception e)
			{
				return OperationResult(OnError(e));
			}
		}

		public ActionResult Download(string username, string path)
		{
			try
			{
				if (OnFileDownloading(username, path))
				{
					var file = ServiceProvider.GetFileService(username).Retrieve(path);

					if (file != null)
					{
						if (file.Stream != null)
							return File(file.Stream, file.ContentType, Server.UrlEncode(file.Name));
						else if (file.Path != null)
							return File(file.Path, file.ContentType, Server.UrlEncode(file.Name));
						else if (file.Data != null)
							return File(file.Data, file.ContentType, Server.UrlEncode(file.Name));
					}
				}
			}
			catch (Exception e)
			{
				OnError(e);
			}

			// Requested file not found
			return NotFound();
		}

		[Authorize]
		public virtual ActionResult FileList(string path)
		{
			var result = OpResult.Denied;

			try
			{
				if (OnFileListing(path, ref result))
				{
					var fileService = ServiceProvider.GetFileService(Username);

					var files = fileService.GetFileList(path);

					return Json(files.Select(f => f.ToJson()));
				}

				return OperationResult(result);
			}
			catch (Exception e)
			{
				return OperationResult(OnError(e));
			}
		}

		[Authorize]
		public virtual ActionResult FileUpload(string path, HttpPostedFileBase fileUploaded)
		{
			var result = OpResult.Denied;

			try
			{
				if (OnFileUploading(path, fileUploaded, ref result))
				{
					var setting = SettingRepository.GetSetting(Username);
					var fileService = ServiceProvider.GetFileService(Username);

					if (!setting.IsExtAllowed(Path.GetExtension(fileUploaded.FileName)))
						result = OpResult.FileTypeNotAllowed;
					else if (fileUploaded.ContentLength > setting.MaxSizePerFile)
						result = OpResult.FileSizeLimitExceed;
					else if (fileService.Count() >= setting.MaxFiles)
						result = OpResult.FileLimitExceed;
					else if (fileService.GetSize() + fileUploaded.ContentLength >= setting.Quota)
						result = OpResult.QuotaExceed;
					else
					{
						result = fileService.Save(path, fileUploaded, false);
						if (result == OpResult.Succeed)
						{
							
							return View(UserFileInfo.FromPostedFile(fileUploaded).ToJson());
						}
					}
				}
			}
			catch (Exception e)
			{
				result = OnError(e);
			}

			return View(new
				{
					succeed = result == OpResult.Succeed,
					status = result.ToString()
				});
		}

		[Authorize]
		public virtual ActionResult FileMove(string srcPath, string destPath)
		{
			var result = OpResult.Denied;

			try
			{
				if (OnFileMoving(srcPath, destPath, ref result))
				{
					var fileService = ServiceProvider.GetFileService(Username);

					result = fileService.Move(srcPath, destPath, false);
				}
			}
			catch (Exception e)
			{
				result = OnError(e);
			}

			return OperationResult(result);
		}

		[Authorize]
		public virtual ActionResult FileDelete(string path)
		{
			var result = OpResult.Denied;

			try
			{
				if (OnFileDeleting(path, ref result))
				{
					var fileService = ServiceProvider.GetFileService(Username);

					result = fileService.Delete(path);
				}
			}
			catch (Exception e)
			{
				result = OnError(e);
			}

			return OperationResult(result);
		}

		protected virtual bool OnFileDownloading(string username, string path)
		{
			return true;
		}

		protected virtual bool OnFolderCreating(string path, ref OpResult result)
		{
			return true;
		}

		protected virtual bool OnFolderMoving(string srcPath, string destPath, ref OpResult result)
		{
			return true;
		}

		protected virtual bool OnFolderDeleting(string path, ref OpResult result)
		{
			return true;
		}

		protected virtual bool OnFolderListing(ref OpResult result)
		{
			return true;
		}

		protected virtual bool OnFileListing(string path, ref OpResult result)
		{
			return true;
		}

		protected virtual bool OnFileUploading(string path, HttpPostedFileBase fileUploaded, ref OpResult result)
		{
			return true;
		}

		protected virtual bool OnFileMoving(string srcPath, string destPath, ref OpResult result)
		{
			return true;
		}

		protected virtual bool OnFileDeleting(string path, ref OpResult result)
		{
			return true;
		}

		protected virtual OpResult OnError(Exception exception)
		{
			return OpResult.Error;
		}

		protected virtual ActionResult OperationResult(OpResult result)
		{
			return Json(
				new
				{
					succeed = result == OpResult.Succeed,
					status = result.ToString()
				}
			);
		}

		protected virtual ActionResult NotFound()
		{
			return new NotFoundResult();
		}
	}
}
